Skip to content

Improve device-init grouped linear module with single grouped weight support #3224

Open
zhongbozhu wants to merge 9 commits into
NVIDIA:mainfrom
zhongbozhu:improve_device_grouped_linear
Open

Improve device-init grouped linear module with single grouped weight support #3224
zhongbozhu wants to merge 9 commits into
NVIDIA:mainfrom
zhongbozhu:improve_device_grouped_linear

Conversation

@zhongbozhu

Copy link
Copy Markdown
Collaborator

Description

Please include a brief summary of the changes, relevant motivation and context.

Fixes # (issue)

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

Please list the changes introduced in this PR:

  • Change A
  • Change B

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@github-actions github-actions Bot added the community-contribution PRs from external contributor outside the core maintainers, representing community-driven work. label Jul 20, 2026
@zhongbozhu
zhongbozhu force-pushed the improve_device_grouped_linear branch from aa3b9d1 to 47ba66a Compare July 20, 2026 22:36
EXPERIMENTAL and subject to change. Gated by the
``NVTE_GROUPED_LINEAR_SINGLE_PARAM`` environment variable: if the env var
is not set this argument is forced to ``False`` with a warning.
grouped_gemm_backend : {"legacy", "grouped_tensor"}, default = None

@timmoon10 timmoon10 Jul 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unintuitive that grouped_gemm_backend="grouped_tensor" might end up using split-quantize internally. What do you think about the following:

  • "split_tensors": Use split-quantize impl
  • "grouped_tensor": User promises split points are valid for grouped GEMM kernel. Use grouped tensor impl, or error out if not supported.
  • "prefer_grouped_tensor": User promises split points are valid for grouped GEMM kernel. Use grouped tensor impl, or fallback to split-quantize impl if not supported.

@zhongbozhu
zhongbozhu force-pushed the improve_device_grouped_linear branch from ff7eee2 to a43f70f Compare July 20, 2026 22:53
Comment thread transformer_engine/pytorch/module/grouped_linear.py
zhongbozhu and others added 7 commits July 23, 2026 03:03
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
@zhongbozhu
zhongbozhu force-pushed the improve_device_grouped_linear branch from 6536aa1 to 1169d6e Compare July 23, 2026 10:26
is_grad_enabled = torch.is_grad_enabled()
num_gemms = self.num_gemms

if FP8GlobalStateManager.fp8_graph_capturing():

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this code block was deleted because it was duplicated

@zhongbozhu
zhongbozhu marked this pull request as ready for review July 23, 2026 10:52
Signed-off-by: Zhongbo Zhu <zhongboz@nvidia.com>
@zhongbozhu

Copy link
Copy Markdown
Collaborator Author

/te-ci pytorch

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves the device-init grouped linear module by adding full support for a single grouped weight/bias parameter (single_grouped_weight / single_grouped_bias) through the native GroupedTensor GEMM backend. It also introduces an explicit grouped_gemm_backend argument to GroupedLinear, deprecates the NVTE_GROUPED_LINEAR_USE_FUSED_GROUPED_GEMM environment variable, and fixes an MXFP8 scale-address bug in the group_quantize_mxfp8 CUDA kernel for single-tensor uniform-group layouts.

  • Single grouped weight/bias: _GroupedLinear._prepare_weights_for_grouped_tensor_gemm and _prepare_bias_for_grouped_tensor_gemm handle both discrete per-expert tensors and a single stacked GroupedTensor parameter; the backward mirrors this by recovering per-group gradients via rowwise_data views.
  • MXFP8 weight caching: tex.group_quantize gains an output= reuse argument (with quantizer-identity and shape validation in C++) and a noop_flag path so CUDA-graph-captured weight quantization can be replayed in-place without pointer changes.
  • Bug fixout_features = weights[0].size(-2) replaces .size(0), which incorrectly returned the expert count for a 3-D grouped-weight tensor.

Confidence Score: 4/5

The changes are well-structured and covered by targeted tests; the main risks are the unguarded quantizer-internal flag mutation and test helpers that would silently skip on future GPU generations.

The core forward/backward plumbing for single grouped parameters is logically consistent and the new tests exercise the key paths (BF16, MXFP8, fuse_wgrad, delay_wgrad, return_bias). The weight_quantizer.internal flag is mutated without a try/finally guard — an exception during quantization would leave it in the wrong state for all subsequent calls. The device-capability upper bound <= (11, 0) in both test helpers is tighter than the production gating, meaning tests would silently skip on SM >= 12.x hardware. The CUDA kernel integer division first_logical_dim / num_tensors lacks an assertion, though the invariant is enforced upstream.

transformer_engine/pytorch/module/grouped_linear.py (quantizer-internal exception safety) and the two test helper functions in test_grouped_linear.py and test_grouped_mlp.py (device capability upper bound).

Important Files Changed

Filename Overview
transformer_engine/pytorch/module/grouped_linear.py Core module refactored to support a single grouped weight/bias parameter alongside discrete per-GEMM parameters; adds grouped_gemm_backend selection, new weight/bias preparation helpers, corrects out_features extraction (.size(-2) vs .size(0)), and wires single-grouped-weight wgrad through GroupedTensorStorage in the backward.
transformer_engine/pytorch/ops/basic/grouped_linear.py Ops-level GroupedLinear replaces split_into_quantized_tensors() usage with direct parameter access, adds _get_packed_bias_tensor() helper, and guards the legacy path against single-grouped parameters.
transformer_engine/common/cast/mxfp8/group_quantize_mxfp8.cuh Fixes MXFP8 scale-address computation for single-tensor (SAME_BOTH_DIMS) grouped quantization by deriving per-member row count and base offset rather than treating the whole payload as one block.
transformer_engine/pytorch/csrc/extensions/cast.cpp Extends group_quantize() to accept an optional output GroupedTensor for in-place reuse; validates quantizer identity and shape compatibility before reusing, enabling CUDA-graph-safe weight caching.
transformer_engine/pytorch/csrc/extensions/pybind.cpp Adds output=py::none() default argument to group_quantize pybind11 binding.
transformer_engine/pytorch/csrc/extensions.h Adds const py::object &output parameter to group_quantize declaration to match the new implementation signature.
transformer_engine/pytorch/ops/fused/grouped_mlp.py Replaces the inline _get_bias_tensors()/stack() pair with the new _get_packed_bias_tensor() helper for FC2 dbias computation.
tests/pytorch/test_grouped_linear.py Adds a helper to gate tests on native grouped-tensor GEMM hardware, and new tests covering single-grouped-weight/bias forward+backward correctness, MXFP8 workspace caching, and return_bias semantics.
tests/pytorch/test_grouped_mlp.py Adds grouped_tensor_path_supported() helper and a test verifying _get_packed_bias_tensor() returns a view of the registered parameter, with a skip guard for unsupported single-grouped-parameter configurations.
tests/pytorch/test_grouped_tensor.py Adds a comprehensive test covering group_quantize in-place reuse and noop_flag behaviour, including CUDA graph capture/replay to validate the weight-caching contract.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant GroupedLinear_Module
    participant _GroupedLinear_Autograd
    participant PrepareWeights
    participant tex_group_quantize
    participant GroupedGEMM

    Caller->>GroupedLinear_Module: forward(x, m_splits, is_first_microbatch)
    GroupedLinear_Module->>GroupedLinear_Module: resolve grouped_gemm_backend
    GroupedLinear_Module->>GroupedLinear_Module: get weight/bias tensors (single or discrete)
    GroupedLinear_Module->>_GroupedLinear_Autograd: "apply(inp, m_splits, non_tensor_args, *weights, *biases)"

    _GroupedLinear_Autograd->>PrepareWeights: "_prepare_weights_for_grouped_tensor_gemm(single_grouped_weight=True)"
    alt BF16 weight
        PrepareWeights-->>_GroupedLinear_Autograd: GroupedTensorStorage (view)
    else MXFP8 weight, workspace cached
        PrepareWeights->>tex_group_quantize: "group_quantize(src, quantizer, N, noop_flag, output=workspace)"
        tex_group_quantize-->>PrepareWeights: workspace (in-place, same pointers)
        PrepareWeights-->>_GroupedLinear_Autograd: cached GroupedTensorStorage
    else MXFP8 weight, no workspace
        PrepareWeights->>tex_group_quantize: group_quantize(src, quantizer, N)
        tex_group_quantize-->>PrepareWeights: new GroupedTensorStorage
        PrepareWeights-->>_GroupedLinear_Autograd: new workspace (saved to _fp8_workspaces)
    end

    _GroupedLinear_Autograd->>GroupedGEMM: general_grouped_gemm_for_grouped_tensor(weight, x, out)
    GroupedGEMM-->>_GroupedLinear_Autograd: output tensor

    _GroupedLinear_Autograd-->>GroupedLinear_Module: (output, new_workspaces)
    GroupedLinear_Module->>GroupedLinear_Module: update _fp8_workspaces["weight"]

    alt return_bias and single_grouped_bias
        GroupedLinear_Module-->>Caller: (output, bias_GroupedTensor)
    else
        GroupedLinear_Module-->>Caller: output
    end
Loading

Comments Outside Diff (1)

  1. transformer_engine/common/cast/mxfp8/group_quantize_mxfp8.cuh, line 786-791 (link)

    P2 Implicit divisibility assumption with no assertion. first_logical_dim / num_tensors uses integer truncation. For the SAME_BOTH_DIMS shape representation all tensors are required to have equal row counts, so the division should always be exact, but there is no compile-time or runtime guard. A silent truncation would produce incorrect per-member scale bases and corrupt the MXFP8 quantization without any visible error. Consider adding a debug-mode assertion such as NVTE_CHECK(first_logical_dim % num_tensors == 0, "...").

Reviews (1): Last reviewed commit: "lint" | Re-trigger Greptile

Comment thread transformer_engine/pytorch/module/grouped_linear.py
Comment on lines +1507 to +1511
def _require_native_grouped_tensor_gemm(*, mxfp8: bool = False) -> None:
"""Skip unless the native GroupedTensor grouped GEMM is available."""
device_capability = torch.cuda.get_device_capability()
if not (9, 0) <= device_capability <= (11, 0):
pytest.skip("Native GroupedTensor grouped GEMM requires Hopper or Blackwell.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Upper-bound capability check blocks future architectures. The <= (11, 0) cap means any GPU with SM ≥ 12.x will be unconditionally skipped even if it supports the native grouped-tensor GEMM. The production path in _is_grouped_tensor_path_supported checks (9, 0) <= capability with no upper bound, so the test guard is more restrictive than the implementation. Consider removing or raising the upper bound.

Suggested change
def _require_native_grouped_tensor_gemm(*, mxfp8: bool = False) -> None:
"""Skip unless the native GroupedTensor grouped GEMM is available."""
device_capability = torch.cuda.get_device_capability()
if not (9, 0) <= device_capability <= (11, 0):
pytest.skip("Native GroupedTensor grouped GEMM requires Hopper or Blackwell.")
def _require_native_grouped_tensor_gemm(*, mxfp8: bool = False) -> None:
"""Skip unless the native GroupedTensor grouped GEMM is available."""
device_capability = torch.cuda.get_device_capability()
if not device_capability >= (9, 0):
pytest.skip("Native GroupedTensor grouped GEMM requires Hopper or newer.")

Comment on lines +141 to +143
compute_capability = get_device_compute_capability()
if not (9, 0) <= compute_capability <= (11, 0):
return False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Same upper-bound issue as in test_grouped_linear.py. The check (9, 0) <= compute_capability <= (11, 0) will return False for any SM ≥ 12.0, causing single_grouped_weight and single_grouped_bias test cases to be silently skipped on future GPU generations even if the backend supports them.

Suggested change
compute_capability = get_device_compute_capability()
if not (9, 0) <= compute_capability <= (11, 0):
return False
compute_capability = get_device_compute_capability()
if not compute_capability >= (9, 0):
return False

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution PRs from external contributor outside the core maintainers, representing community-driven work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants